package com.xxx.redismq.common.utils;

import javax.annotation.Resource;

import com.xxx.redismq.common.pojo.Message;
import com.xxx.redismq.common.pojo.Route;

import java.util.ArrayList;
import java.util.List;

/**
 * RedisMQ管理工具类
 * 基于redis的消息队列对象 将整个redis作为消息池存储消息体，以ZSET为消息队列，LIST作为待消费列表
 * 用Spring定时器作为监听器，每次监听ZSET中指定数量的消息
 * 根据SCORE确定是否达到发送要求，如果达到，利用消息路由将消息路由到待消费list
 */
public class RedisMqUtil {

	/**
	 * 消息池前缀，以此前缀加上传递的消息id作为key，以消息{@link Message} 的消息体body作为值存储
	 */
	private static final String MSG_POOL = "Message:Pool:";

	/**
	 * 默认监听数量，对应监听zset队列前多少个元素
	 */
	private static final int DEFAUT_MONITOR = 10;

	/**
	 * 每次监听queue中元素的数量，可配置
	 */
	private int monitorCount = DEFAUT_MONITOR;

	/**
	 * 消息路由
	 */
	private List<Route> routes;

	@Resource
	private JedisUtils jedisUtils;

	/**
	 * 存入消息池
	 * @param message
	 * @return
	 */
	public boolean addMsgIntoPool(Message message) {
		if (null != message) {
			return jedisUtils.setex(MSG_POOL + message.getId(), message.getBody(), message.getTtl());
		}
		return false;
	}

	/**
	 * 从消息池中删除消息
	 * @param id
	 * @return
	 */
	public boolean delMsgFromPool(String id) {
		return jedisUtils.del(MSG_POOL + id);
	}

	/**
	 * 向zset中添加消息
	 * @param key
	 * @param score 优先级
	 * @param val
	 * @return 返回消息id
	 */
	public String addMsgIntoMq(String key, long score, String val) {
		if (jedisUtils.zadd(key, score, val)) {
			return val;
		}
		return "";
	}

	/**
	 * 从zset删除消息
	 * @param id
	 * @return
	 */
	public boolean delMsgFromMq(String key, String id) {
		return jedisUtils.zdel(key, id);
	}

	/**
	 * 消费消息，就是将消息从待消费list里取出来，并返回给消费者，同时从待消费list里删除
	 * @return
	 */
	public List<String> consume(String key) {
		long count = jedisUtils.countList(key);
		if (count>0) {
			// 可根据需求做限制
			List<String> ids = jedisUtils.rangeList(key, 0, count - 1);
			if (ids != null) {
				List<String> result = new ArrayList<>();
				ids.forEach(l -> result.add(jedisUtils.get(MSG_POOL + l, "")));
				jedisUtils.removeListValue(key, ids);
				return result;
			}
		}
		return null;
	}

	public int getMonitorCount() {
		return monitorCount;
	}

	public void setMonitorCount(int monitorCount) {
		this.monitorCount = monitorCount;
	}

	public List<Route> getRoutes() {
		return routes;
	}

	public void setRoutes(List<Route> routes) {
		this.routes = routes;
	}
}
